//#include <list>
#include <set>

#include "SceneManager.h"
//#include "Scene.h"
#include "Transformation.h"
#include "WanderInRegion.h"
#include "Exception.h"
#include "Logger.h"

using namespace game_engine;
using namespace game_engine::components;
using namespace game_engine::library;
using namespace game_engine::utils;

///////////////////////////////////////////////////////////////////////////////
// Constructors and Destructors
///////////////////////////////////////////////////////////////////////////////

SceneManager::SceneManager(void)
    : scene(NULL), collisionArbiter()
{
    slotMovedObject.AssignFunction(this, &SceneManager::addMovedObject);
}

SceneManager::SceneManager(Scene *parent)
    : scene(parent), collisionArbiter()
{
    slotMovedObject.AssignFunction(this, &SceneManager::addMovedObject);
}

SceneManager::~SceneManager(void)
{
}

///////////////////////////////////////////////////////////////////////////////
// Other public methods
///////////////////////////////////////////////////////////////////////////////

//void SceneManager::initialize(void) throw(...)
//{
//    // This is where all connections to signals are defined
//    // IMPORTANT : all objects connecting to the addMovedObject slot, must have
//    // a BoxCollider component
//    vector<GameObject*> objs = objectsWithCollider();
//    vector<GameObject*>::iterator i;
//    for (i=objs.begin(); i!=objs.end(); i++)
//    {
//        Transformation *transf = dynamic_cast<Transformation*> 
//            ((*i)->getComponent("Transformation"));
//        slotMovedObject.Connect(transf->signalPositionUpdated);
//    }
//}
//
//void SceneManager::update(void) throw(...)
//{
//    // If any object has moved
//    if (!movedObjects.empty())
//    {
//        // Get the list of moved objects which collide with the world
//        vector<GameObject*> objToWorld;
//        try
//        {
//            objToWorld = collisionArbiter.evaluateWorldCollisions(scene, 
//                movedObjects);
//        } catch (exception &e)
//        {
//            Logger::ERR(GAMEENGINE_LOG, e.what());
//            movedObjects.clear();
//            throw RuntimeException(e.what());
//        }
//
//        // Carry out the appropriate actions with those objects
//        vector<GameObject*>::iterator i;
//        for (i=objToWorld.begin(); i<objToWorld.end(); i++)
//        {
//            processWorldCollision(*i);
//        }
//
//        // Clear list of moved objects
//        movedObjects.clear();
//
//        // Get the list of pairs of objects which collide with each other
//        // All objects for which a BoxCollider had been defined must be analyzed
//        vector<GameObject*> objs = objectsWithCollider();
//        vector<pair<GameObject*, GameObject*>> objCollisions;
//        try
//        {
//             objCollisions = collisionArbiter.evaluateInterCollisions(objs);
//        } catch (exception &e)
//        {
//            Logger::ERR(GAMEENGINE_LOG, e.what());
//            throw RuntimeException(e.what());
//        }
//
//        // TODO : Carry out the appropriate actions with those objects
//
//    }
//}

//void SceneManager::finalize(void) throw(...)
//{
//    // This is where all connections to signals are destroyed
//    slotMovedObject.DisconnectAll();
//}

//BOOL SceneManager::worldCollision(GameObject *obj, const TileType tileType)
//{
//    // Get Transformation component
//    Transformation *objTransf = dynamic_cast<Transformation*> 
//        (obj->getComponent("Transformation"));
//
//    // Perform action according to tile type
//    switch(tileType)
//    {
//    case UNWALKABLE:
//        // Revert the object's position
//        objTransf->revertPosition();
//
//        // Specific case for wandering objects
//        if (obj->hasComponent("WanderInRegion"))
//        {
//            WanderInRegion *wander = dynamic_cast<WanderInRegion*> 
//                (obj->getComponent("WanderInRegion"));
//            wander->forceDestinationReached();
//        }
//
//        break;
//    default:
//        break;
//    }
//
//    return true;
//}

///////////////////////////////////////////////////////////////////////////////
// Slots
///////////////////////////////////////////////////////////////////////////////

INT32 SceneManager::addMovedObject(GameObject *obj)
{
    movedObjects.push_back(obj);
    return 0;
}

///////////////////////////////////////////////////////////////////////////////
// Protected methods
///////////////////////////////////////////////////////////////////////////////

void SceneManager::processWorldCollision(GameObject *obj)
{
    // Get Transformation component
    Transformation *objTransf = dynamic_cast<Transformation*> 
        (obj->getComponent("Transformation"));

    // Revert the object's position
    objTransf->revertPosition();

    // Specific case for wandering objects
    if (obj->hasComponent("WanderInRegion"))
    {
        WanderInRegion *wander = dynamic_cast<WanderInRegion*> 
            (obj->getComponent("WanderInRegion"));
        wander->forceDestinationReached();
    }
}

const vector<GameObject*> SceneManager::objectsWithCollider(void) const
{
    vector<GameObject*> objs;

    list<GameObject*> allObjects = scene->getObjects();
    list<GameObject*>::iterator i;
    for (i=allObjects.begin(); i!=allObjects.end(); i++)
    {
        if ((*i)->hasComponent("BoxCollider") && (*i)->hasComponent("Transformation"))
        {
            objs.push_back((*i));
        }
    }

    return objs;
}

///////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////

//const vector<GameObject*> SceneManager::objectsWithCollider(void) const
//{
//    vector<GameObject*> objs;
//
//    list<GameObject*> allObjects = scene->getObjects();
//    list<GameObject*>::iterator i;
//    for (i=allObjects.begin(); i!=allObjects.end(); i++)
//    {
//        if ((*i)->hasComponent("BoxCollider") && (*i)->hasComponent("Transformation"))
//        {
//            objs.push_back((*i));
//        }
//    }
//
//    return objs;
//}

///////////////////////////////////////////////////////////////////////////////
// Setters
///////////////////////////////////////////////////////////////////////////////

void SceneManager::setScene(Scene *parent)
{
    scene = parent;
}
